package com.hqd.jjvm.classformat;

import com.hqd.jjvm.rtda.heap.JClass;
import com.hqd.jjvm.rtda.heap.JMethod;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import org.apache.commons.lang.StringUtils;

/**
 * class文件校验器
 *
 * @author hqd
 */
@AllArgsConstructor
@NoArgsConstructor
public class JClassChecker {
    protected static final String MAGIC_NUMBER = "cafebabe";
    protected static final Float MAJOR_VERSION = Float.valueOf(System.getProperty("java.class.version"));
    private ClassFile classFile;


    /**
     * 检查常量池引用
     *
     * @param jClass
     */
    public void checkSymbolRef(JClass jClass) {

    }

    /**
     * 检查指令
     *
     * @param jClass
     */
    public void checkOperation(JClass jClass) {
    }

    /**
     * 检验访问标识
     *
     * @param jClass
     */
    public void checkAccessFlag(JClass jClass) {
    }

    /**
     * 语法校验
     * 1)final类无法被继承
     * 2)final方法无法被重写
     * 3)派生类方法签名一致，返回值却不同
     * 4)Object不包含父类，其他类包含父类
     *
     * @param jClass
     */
    public void checkSyntax(JClass jClass) {
        JClass superJClass = jClass.getSuperClass();
        if (!jClass.getClassName().equals("java/lang/Object") && superJClass == null) {
            throw new ClassFormatError(String.format("Invalid this class index 0 in constant pool in class file %s ", jClass.getClassName()));
        }
        if (jClass.getClassName().equals("java/lang/Object") && superJClass != null) {
            throw new ClassFormatError(String.format("Invalid this class index 0 in constant pool in class file %s ", jClass.getClassName()));
        }
        if (superJClass != null) {
            if (superJClass.isFinal()) {
                throw new VerifyError(String.format("%s super class is final", jClass.getClassName()));
            }
            JMethod[] jMethods = superJClass.getJMethods();
            for (JMethod jMethod : jMethods) {
                JMethod jm = jClass.getJMethod(jMethod.getName(), jMethod.getDescriptor());
                if (jm != null) {
                    if (jMethod.isFinal()) {
                        throw new VerifyError(String.format("class: %s  overrides final method %s.%s", jClass.getClassName(), jm.getName(), jm.getDescriptor()));
                    }
                } else {
                    if (jm != null && !jm.getDescriptor().equals(jMethod.getDescriptor())) {
                        //throw new VerifyError("Method expects a return value");
                    }
                }
            }
            checkSyntax(superJClass);
        }
    }

    /**
     * 检查魔数
     *
     * @param magicNumber
     * @return
     */
    protected void checkMagicNumber(String magicNumber) {
        if (!MAGIC_NUMBER.equals(magicNumber)) {
            throw new ClassFormatError(String.format("Incompatible magic value %s in class file %s", magicNumber, classFile.getClassFileInfo().getClassPath()));
        }
    }

    /**
     * 检查版本号
     *
     * @param majorVersion
     * @param minorVersion
     */
    protected void checkClassVersion(float majorVersion, float minorVersion) {
        if (majorVersion != MAJOR_VERSION) {
            //	throw new UnsupportedOperationException(String.format("%s has been compiled by a more recent version of the Java Runtime (class file version %s), this version of the Java Runtime only recognizes class file versions up to %s", classFile.getClassFileInfo().getClassPath(), majorVersion, JClassChecker.MAJOR_VERSION));
        }
    }

    /**
     * 检查是否正确结尾
     */
    public void checkClassFileEnd() {
        if (StringUtils.isNotBlank(classFile.getHexStr())) {
            throw new ClassFormatError(String.format(" Extra bytes at the end of class file %s ", classFile.getClassFileInfo().getClassPath()));
        }
    }
}
